home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / ui_qmenu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  35.3 KB  |  1,699 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. /**********************************************************************
  4.     UI_QMENU.C
  5.  
  6.     Quake's menu framework system.
  7. **********************************************************************/
  8. #include "ui_local.h"
  9.  
  10. sfxHandle_t menu_in_sound;
  11. sfxHandle_t menu_move_sound;
  12. sfxHandle_t menu_out_sound;
  13. sfxHandle_t menu_buzz_sound;
  14. sfxHandle_t menu_null_sound;
  15.  
  16. static qhandle_t    sliderBar;
  17. static qhandle_t    sliderButton_0;
  18. static qhandle_t    sliderButton_1;
  19.  
  20. vec4_t menu_text_color        = {1.0, 1.0, 1.0, 1.0};
  21. vec4_t menu_dim_color       = {0.0, 0.0, 0.0, 0.75};
  22. vec4_t color_black            = {0.00, 0.00, 0.00, 1.00};
  23. vec4_t color_white            = {1.00, 1.00, 1.00, 1.00};
  24. vec4_t color_yellow            = {1.00, 1.00, 0.00, 1.00};
  25. vec4_t color_blue            = {0.00, 0.00, 1.00, 1.00};
  26. vec4_t color_lightOrange    = {1.00, 0.68, 0.00, 1.00 };
  27. vec4_t color_orange            = {1.00, 0.43, 0.00, 1.00};
  28. vec4_t color_red            = {1.00, 0.00, 0.00, 1.00};
  29. vec4_t color_dim            = {0.00, 0.00, 0.00, 0.25};
  30.  
  31. // current color scheme
  32. vec4_t pulse_color          = {1.00, 1.00, 1.00, 1.00};
  33. vec4_t text_color_disabled  = {0.50, 0.50, 0.50, 1.00};    // light gray
  34. vec4_t text_color_normal    = {1.00, 0.43, 0.00, 1.00};    // light orange
  35. vec4_t text_color_highlight = {1.00, 1.00, 0.00, 1.00};    // bright yellow
  36. vec4_t listbar_color        = {1.00, 0.43, 0.00, 0.30};    // transluscent orange
  37. vec4_t text_color_status    = {1.00, 1.00, 1.00, 1.00};    // bright white    
  38.  
  39. // action widget
  40. static void    Action_Init( menuaction_s *a );
  41. static void    Action_Draw( menuaction_s *a );
  42.  
  43. // radio button widget
  44. static void    RadioButton_Init( menuradiobutton_s *rb );
  45. static void    RadioButton_Draw( menuradiobutton_s *rb );
  46. static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key );
  47.  
  48. // slider widget
  49. static void Slider_Init( menuslider_s *s );
  50. static sfxHandle_t Slider_Key( menuslider_s *s, int key );
  51. static void    Slider_Draw( menuslider_s *s );
  52.  
  53. // spin control widget
  54. static void    SpinControl_Init( menulist_s *s );
  55. static void    SpinControl_Draw( menulist_s *s );
  56. static sfxHandle_t SpinControl_Key( menulist_s *l, int key );
  57.  
  58. // text widget
  59. static void Text_Init( menutext_s *b );
  60. static void Text_Draw( menutext_s *b );
  61.  
  62. // scrolllist widget
  63. static void    ScrollList_Init( menulist_s *l );
  64. sfxHandle_t ScrollList_Key( menulist_s *l, int key );
  65.  
  66. // proportional text widget
  67. static void PText_Init( menutext_s *b );
  68. static void PText_Draw( menutext_s *b );
  69.  
  70. // proportional banner text widget
  71. static void BText_Init( menutext_s *b );
  72. static void BText_Draw( menutext_s *b );
  73.  
  74. /*
  75. =================
  76. Text_Init
  77. =================
  78. */
  79. static void Text_Init( menutext_s *t )
  80. {
  81.     t->generic.flags |= QMF_INACTIVE;
  82. }
  83.  
  84. /*
  85. =================
  86. Text_Draw
  87. =================
  88. */
  89. static void Text_Draw( menutext_s *t )
  90. {
  91.     int        x;
  92.     int        y;
  93.     char    buff[512];    
  94.     float*    color;
  95.  
  96.     x = t->generic.x;
  97.     y = t->generic.y;
  98.  
  99.     buff[0] = '\0';
  100.  
  101.     // possible label
  102.     if (t->generic.name)
  103.         strcpy(buff,t->generic.name);
  104.  
  105.     // possible value
  106.     if (t->string)
  107.         strcat(buff,t->string);
  108.         
  109.     if (t->generic.flags & QMF_GRAYED)
  110.         color = text_color_disabled;
  111.     else
  112.         color = t->color;
  113.  
  114.     UI_DrawString( x, y, buff, t->style, color );
  115. }
  116.  
  117. /*
  118. =================
  119. BText_Init
  120. =================
  121. */
  122. static void BText_Init( menutext_s *t )
  123. {
  124.     t->generic.flags |= QMF_INACTIVE;
  125. }
  126.  
  127. /*
  128. =================
  129. BText_Draw
  130. =================
  131. */
  132. static void BText_Draw( menutext_s *t )
  133. {
  134.     int        x;
  135.     int        y;
  136.     float*    color;
  137.  
  138.     x = t->generic.x;
  139.     y = t->generic.y;
  140.  
  141.     if (t->generic.flags & QMF_GRAYED)
  142.         color = text_color_disabled;
  143.     else
  144.         color = t->color;
  145.  
  146.     UI_DrawBannerString( x, y, t->string, t->style, color );
  147. }
  148.  
  149. /*
  150. =================
  151. PText_Init
  152. =================
  153. */
  154. static void PText_Init( menutext_s *t )
  155. {
  156.     int    x;
  157.     int    y;
  158.     int    w;
  159.     int    h;
  160.     float    sizeScale;
  161.  
  162.     sizeScale = UI_ProportionalSizeScale( t->style );
  163.  
  164.     x = t->generic.x;
  165.     y = t->generic.y;
  166.     w = UI_ProportionalStringWidth( t->string ) * sizeScale;
  167.     h =    PROP_HEIGHT * sizeScale;
  168.  
  169.     if( t->generic.flags & QMF_RIGHT_JUSTIFY ) {
  170.         x -= w;
  171.     }
  172.     else if( t->generic.flags & QMF_CENTER_JUSTIFY ) {
  173.         x -= w / 2;
  174.     }
  175.  
  176.     t->generic.left   = x - PROP_GAP_WIDTH * sizeScale;
  177.     t->generic.right  = x + w + PROP_GAP_WIDTH * sizeScale;
  178.     t->generic.top    = y;
  179.     t->generic.bottom = y + h;
  180. }
  181.  
  182. /*
  183. =================
  184. PText_Draw
  185. =================
  186. */
  187. static void PText_Draw( menutext_s *t )
  188. {
  189.     int        x;
  190.     int        y;
  191.     float *    color;
  192.     int        style;
  193.  
  194.     x = t->generic.x;
  195.     y = t->generic.y;
  196.  
  197.     if (t->generic.flags & QMF_GRAYED)
  198.         color = text_color_disabled;
  199.     else
  200.         color = t->color;
  201.  
  202.     style = t->style;
  203.     if( t->generic.flags & QMF_PULSEIFFOCUS ) {
  204.         if( Menu_ItemAtCursor( t->generic.parent ) == t ) {
  205.             style |= UI_PULSE;
  206.         }
  207.         else {
  208.             style |= UI_INVERSE;
  209.         }
  210.     }
  211.  
  212.     UI_DrawProportionalString( x, y, t->string, style, color );
  213. }
  214.  
  215. /*
  216. =================
  217. Bitmap_Init
  218. =================
  219. */
  220. void Bitmap_Init( menubitmap_s *b )
  221. {
  222.     int    x;
  223.     int    y;
  224.     int    w;
  225.     int    h;
  226.  
  227.     x = b->generic.x;
  228.     y = b->generic.y;
  229.     w = b->width;
  230.     h =    b->height;
  231.     if( w < 0 ) {
  232.         w = -w;
  233.     }
  234.     if( h < 0 ) {
  235.         h = -h;
  236.     }
  237.  
  238.     if (b->generic.flags & QMF_RIGHT_JUSTIFY)
  239.     {
  240.         x = x - w;
  241.     }
  242.     else if (b->generic.flags & QMF_CENTER_JUSTIFY)
  243.     {
  244.         x = x - w/2;
  245.     }
  246.  
  247.     b->generic.left   = x;
  248.     b->generic.right  = x + w;
  249.     b->generic.top    = y;
  250.     b->generic.bottom = y + h;
  251.  
  252.     b->shader      = 0;
  253.     b->focusshader = 0;
  254. }
  255.  
  256. /*
  257. =================
  258. Bitmap_Draw
  259. =================
  260. */
  261. void Bitmap_Draw( menubitmap_s *b )
  262. {
  263.     float    x;
  264.     float    y;
  265.     float    w;
  266.     float    h;
  267.     vec4_t    tempcolor;
  268.     float*    color;
  269.  
  270.     x = b->generic.x;
  271.     y = b->generic.y;
  272.     w = b->width;
  273.     h =    b->height;
  274.  
  275.     if (b->generic.flags & QMF_RIGHT_JUSTIFY)
  276.     {
  277.         x = x - w;
  278.     }
  279.     else if (b->generic.flags & QMF_CENTER_JUSTIFY)
  280.     {
  281.         x = x - w/2;
  282.     }
  283.  
  284.     // used to refresh shader
  285.     if (b->generic.name && !b->shader)
  286.     {
  287.         b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
  288.         if (!b->shader && b->errorpic)
  289.             b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
  290.     }
  291.  
  292.     if (b->focuspic && !b->focusshader)
  293.         b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
  294.  
  295.     if (b->generic.flags & QMF_GRAYED)
  296.     {
  297.         if (b->shader)
  298.         {
  299.             trap_R_SetColor( colorMdGrey );
  300.             UI_DrawHandlePic( x, y, w, h, b->shader );
  301.             trap_R_SetColor( NULL );
  302.         }
  303.     }
  304.     else
  305.     {
  306.         if (b->shader)
  307.             UI_DrawHandlePic( x, y, w, h, b->shader );
  308.  
  309.         if ((b->generic.flags & QMF_PULSE) || (b->generic.flags & QMF_PULSEIFFOCUS) && (Menu_ItemAtCursor( b->generic.parent ) == b))
  310.         {    
  311.             if (b->focuscolor)            
  312.             {
  313.                 tempcolor[0] = b->focuscolor[0];
  314.                 tempcolor[1] = b->focuscolor[1];
  315.                 tempcolor[2] = b->focuscolor[2];
  316.                 color        = tempcolor;    
  317.             }
  318.             else
  319.                 color = pulse_color;
  320.             color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
  321.  
  322.             trap_R_SetColor( color );
  323.             UI_DrawHandlePic( x, y, w, h, b->focusshader );
  324.             trap_R_SetColor( NULL );
  325.         }
  326.         else if ((b->generic.flags & QMF_HIGHLIGHT) || ((b->generic.flags & QMF_HIGHLIGHT_IF_FOCUS) && (Menu_ItemAtCursor( b->generic.parent ) == b)))
  327.         {    
  328.             if (b->focuscolor)
  329.             {
  330.                 trap_R_SetColor( b->focuscolor );
  331.                 UI_DrawHandlePic( x, y, w, h, b->focusshader );
  332.                 trap_R_SetColor( NULL );
  333.             }
  334.             else
  335.                 UI_DrawHandlePic( x, y, w, h, b->focusshader );
  336.         }
  337.     }
  338. }
  339.  
  340. /*
  341. =================
  342. Action_Init
  343. =================
  344. */
  345. static void Action_Init( menuaction_s *a )
  346. {
  347.     int    len;
  348.  
  349.     // calculate bounds
  350.     if (a->generic.name)
  351.         len = strlen(a->generic.name);
  352.     else
  353.         len = 0;
  354.  
  355.     // left justify text
  356.     a->generic.left   = a->generic.x; 
  357.     a->generic.right  = a->generic.x + len*BIGCHAR_WIDTH;
  358.     a->generic.top    = a->generic.y;
  359.     a->generic.bottom = a->generic.y + BIGCHAR_HEIGHT;
  360. }
  361.  
  362. /*
  363. =================
  364. Action_Draw
  365. =================
  366. */
  367. static void Action_Draw( menuaction_s *a )
  368. {
  369.     int        x, y;
  370.     int        style;
  371.     float*    color;
  372.  
  373.     style = 0;
  374.     color = menu_text_color;
  375.     if ( a->generic.flags & QMF_GRAYED )
  376.     {
  377.         color = text_color_disabled;
  378.     }
  379.     else if (( a->generic.flags & QMF_PULSEIFFOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
  380.     {
  381.         color = text_color_highlight;
  382.         style = UI_PULSE;
  383.     }
  384.     else if (( a->generic.flags & QMF_HIGHLIGHT_IF_FOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
  385.     {
  386.         color = text_color_highlight;
  387.     }
  388.     else if ( a->generic.flags & QMF_BLINK )
  389.     {
  390.         style = UI_BLINK;
  391.         color = text_color_highlight;
  392.     }
  393.  
  394.     x = a->generic.x;
  395.     y = a->generic.y;
  396.  
  397.     UI_DrawString( x, y, a->generic.name, UI_LEFT|style, color );
  398.  
  399.     if ( a->generic.parent->cursor == a->generic.menuPosition )
  400.     {
  401.         // draw cursor
  402.         UI_DrawChar( x - BIGCHAR_WIDTH, y, 13, UI_LEFT|UI_BLINK, color);
  403.     }
  404. }
  405.  
  406. /*
  407. =================
  408. RadioButton_Init
  409. =================
  410. */
  411. static void RadioButton_Init( menuradiobutton_s *rb )
  412. {
  413.     int    len;
  414.  
  415.     // calculate bounds
  416.     if (rb->generic.name)
  417.         len = strlen(rb->generic.name);
  418.     else
  419.         len = 0;
  420.  
  421.     rb->generic.left   = rb->generic.x - (len+1)*SMALLCHAR_WIDTH;
  422.     rb->generic.right  = rb->generic.x + 6*SMALLCHAR_WIDTH;
  423.     rb->generic.top    = rb->generic.y;
  424.     rb->generic.bottom = rb->generic.y + SMALLCHAR_HEIGHT;
  425. }
  426.  
  427. /*
  428. =================
  429. RadioButton_Key
  430. =================
  431. */
  432. static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key )
  433. {
  434.     switch (key)
  435.     {
  436.         case K_MOUSE1:
  437.             if (!(rb->generic.flags & QMF_HASMOUSEFOCUS))
  438.                 break;
  439.  
  440.         case K_JOY1:
  441.         case K_JOY2:
  442.         case K_JOY3:
  443.         case K_JOY4:
  444.         case K_ENTER:
  445.         case K_KP_ENTER:
  446.         case K_KP_LEFTARROW:
  447.         case K_LEFTARROW:
  448.         case K_KP_RIGHTARROW:
  449.         case K_RIGHTARROW:
  450.             rb->curvalue = !rb->curvalue;
  451.             if ( rb->generic.callback )
  452.                 rb->generic.callback( rb, QM_ACTIVATED );
  453.  
  454.             return (menu_move_sound);
  455.     }
  456.  
  457.     // key not handled
  458.     return 0;
  459. }
  460.  
  461. /*
  462. =================
  463. RadioButton_Draw
  464. =================
  465. */
  466. static void RadioButton_Draw( menuradiobutton_s *rb )
  467. {
  468.     int    x;
  469.     int y;
  470.     float *color;
  471.     int    style;
  472.     qboolean focus;
  473.  
  474.     x = rb->generic.x;
  475.     y = rb->generic.y;
  476.  
  477.     focus = (rb->generic.parent->cursor == rb->generic.menuPosition);
  478.  
  479.     if ( rb->generic.flags & QMF_GRAYED )
  480.     {
  481.         color = text_color_disabled;
  482.         style = UI_LEFT|UI_SMALLFONT;
  483.     }
  484.     else if ( focus )
  485.     {
  486.         color = text_color_highlight;
  487.         style = UI_LEFT|UI_PULSE|UI_SMALLFONT;
  488.     }
  489.     else
  490.     {
  491.         color = text_color_normal;
  492.         style = UI_LEFT|UI_SMALLFONT;
  493.     }
  494.  
  495.     if ( focus )
  496.     {
  497.         // draw cursor
  498.         UI_FillRect( rb->generic.left, rb->generic.top, rb->generic.right-rb->generic.left+1, rb->generic.bottom-rb->generic.top+1, listbar_color ); 
  499.         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
  500.     }
  501.  
  502.     if ( rb->generic.name )
  503.         UI_DrawString( x - SMALLCHAR_WIDTH, y, rb->generic.name, UI_RIGHT|UI_SMALLFONT, color );
  504.  
  505.     if ( !rb->curvalue )
  506.     {
  507.         UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_off);
  508.         UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "off", style, color );
  509.     }
  510.     else
  511.     {
  512.         UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_on );
  513.         UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "on", style, color );
  514.     }
  515. }
  516.  
  517. /*
  518. =================
  519. Slider_Init
  520. =================
  521. */
  522. static void Slider_Init( menuslider_s *s )
  523. {
  524.     int len;
  525.  
  526.     // calculate bounds
  527.     if (s->generic.name)
  528.         len = strlen(s->generic.name);
  529.     else
  530.         len = 0;
  531.  
  532.     s->generic.left   = s->generic.x - (len+1)*SMALLCHAR_WIDTH; 
  533.     s->generic.right  = s->generic.x + (SLIDER_RANGE+2+1)*SMALLCHAR_WIDTH;
  534.     s->generic.top    = s->generic.y;
  535.     s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT;
  536. }
  537.  
  538. /*
  539. =================
  540. Slider_Key
  541. =================
  542. */
  543. static sfxHandle_t Slider_Key( menuslider_s *s, int key )
  544. {
  545.     sfxHandle_t    sound;
  546.     int            x;
  547.     int            oldvalue;
  548.  
  549.     switch (key)
  550.     {
  551.         case K_MOUSE1:
  552.             x           = uis.cursorx - s->generic.x - 2*SMALLCHAR_WIDTH;
  553.             oldvalue    = s->curvalue;
  554.             s->curvalue = (x/(float)(SLIDER_RANGE*SMALLCHAR_WIDTH)) * (s->maxvalue-s->minvalue) + s->minvalue;
  555.  
  556.             if (s->curvalue < s->minvalue)
  557.                 s->curvalue = s->minvalue;
  558.             else if (s->curvalue > s->maxvalue)
  559.                 s->curvalue = s->maxvalue;
  560.             if (s->curvalue != oldvalue)
  561.                 sound = menu_move_sound;
  562.             else
  563.                 sound = 0;
  564.             break;
  565.  
  566.         case K_KP_LEFTARROW:
  567.         case K_LEFTARROW:
  568.             if (s->curvalue > s->minvalue)
  569.             {
  570.                 s->curvalue--;
  571.                 sound = menu_move_sound;
  572.             }
  573.             else
  574.                 sound = menu_buzz_sound;
  575.             break;            
  576.  
  577.         case K_KP_RIGHTARROW:
  578.         case K_RIGHTARROW:
  579.             if (s->curvalue < s->maxvalue)
  580.             {
  581.                 s->curvalue++;
  582.                 sound = menu_move_sound;
  583.             }
  584.             else
  585.                 sound = menu_buzz_sound;
  586.             break;            
  587.  
  588.         default:
  589.             // key not handled
  590.             sound = 0;
  591.             break;
  592.     }
  593.  
  594.     if ( sound && s->generic.callback )
  595.         s->generic.callback( s, QM_ACTIVATED );
  596.  
  597.     return (sound);
  598. }
  599.  
  600. #if 1
  601. /*
  602. =================
  603. Slider_Draw
  604. =================
  605. */
  606. static void Slider_Draw( menuslider_s *s ) {
  607.     int            x;
  608.     int            y;
  609.     int            style;
  610.     float        *color;
  611.     int            button;
  612.     qboolean    focus;
  613.     
  614.     x =    s->generic.x;
  615.     y = s->generic.y;
  616.     focus = (s->generic.parent->cursor == s->generic.menuPosition);
  617.  
  618.     if( s->generic.flags & QMF_GRAYED ) {
  619.         color = text_color_disabled;
  620.         style = UI_SMALLFONT;
  621.     }
  622.     else if( focus ) {
  623.         color  = text_color_highlight;
  624.         style = UI_SMALLFONT | UI_PULSE;
  625.     }
  626.     else {
  627.         color = text_color_normal;
  628.         style = UI_SMALLFONT;
  629.     }
  630.  
  631.     // draw label
  632.     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
  633.  
  634.     // draw slider
  635.     UI_SetColor( color );
  636.     UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y, 96, 16, sliderBar );
  637.     UI_SetColor( NULL );
  638.  
  639.     // clamp thumb
  640.     if( s->maxvalue > s->minvalue )    {
  641.         s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
  642.         if( s->range < 0 ) {
  643.             s->range = 0;
  644.         }
  645.         else if( s->range > 1) {
  646.             s->range = 1;
  647.         }
  648.     }
  649.     else {
  650.         s->range = 0;
  651.     }
  652.  
  653.     // draw thumb
  654.     if( style & UI_PULSE) {
  655.         button = sliderButton_1;
  656.     }
  657.     else {
  658.         button = sliderButton_0;
  659.     }
  660.  
  661.     UI_DrawHandlePic( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ) - 2, y - 2, 12, 20, button );
  662. }
  663. #else
  664. /*
  665. =================
  666. Slider_Draw
  667. =================
  668. */
  669. static void Slider_Draw( menuslider_s *s )
  670. {
  671.     float *color;
  672.     int    style;
  673.     int    i;
  674.     int x;
  675.     int y;
  676.     qboolean focus;
  677.     
  678.     x =    s->generic.x;
  679.     y = s->generic.y;
  680.     focus = (s->generic.parent->cursor == s->generic.menuPosition);
  681.  
  682.     style = UI_SMALLFONT;
  683.     if ( s->generic.flags & QMF_GRAYED )
  684.     {
  685.         color = text_color_disabled;
  686.     }
  687.     else if (focus)
  688.     {
  689.         color  = text_color_highlight;
  690.         style |= UI_PULSE;
  691.     }
  692.     else
  693.     {
  694.         color = text_color_normal;
  695.     }
  696.  
  697.     if ( focus )
  698.     {
  699.         // draw cursor
  700.         UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); 
  701.         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
  702.     }
  703.  
  704.     // draw label
  705.     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
  706.  
  707.     // draw slider
  708.     UI_DrawChar( x + SMALLCHAR_WIDTH, y, 128, UI_LEFT|style, color);
  709.     for ( i = 0; i < SLIDER_RANGE; i++ )
  710.         UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 129, UI_LEFT|style, color);
  711.     UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 130, UI_LEFT|style, color);
  712.  
  713.     // clamp thumb
  714.     if (s->maxvalue > s->minvalue)
  715.     {
  716.         s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
  717.         if ( s->range < 0)
  718.             s->range = 0;
  719.         else if ( s->range > 1)
  720.             s->range = 1;
  721.     }
  722.     else
  723.         s->range = 0;
  724.  
  725.     // draw thumb
  726.     if (style & UI_PULSE) {
  727.         style &= ~UI_PULSE;
  728.         style |= UI_BLINK;
  729.     }
  730.     UI_DrawChar( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ), y, 131, UI_LEFT|style, color);
  731. }
  732. #endif
  733.  
  734. /*
  735. =================
  736. SpinControl_Init
  737. =================
  738. */
  739. static void SpinControl_Init( menulist_s *s ) {
  740.     int    len;
  741.     int    l;
  742.     const char* str;
  743.  
  744.     if (s->generic.name)
  745.         len = strlen(s->generic.name) * SMALLCHAR_WIDTH;
  746.     else
  747.         len = 0;
  748.  
  749.     s->generic.left    = s->generic.x - SMALLCHAR_WIDTH - len;
  750.  
  751.     len = s->numitems = 0;
  752.     while ( (str = s->itemnames[s->numitems]) != 0 )
  753.     {
  754.         l = strlen(str);
  755.         if (l > len)
  756.             len = l;
  757.  
  758.         s->numitems++;
  759.     }        
  760.  
  761.     s->generic.top      =    s->generic.y;
  762.     s->generic.right  =    s->generic.x + (len+1)*SMALLCHAR_WIDTH;
  763.     s->generic.bottom =    s->generic.y + SMALLCHAR_HEIGHT;
  764. }
  765.  
  766. /*
  767. =================
  768. SpinControl_Key
  769. =================
  770. */
  771. static sfxHandle_t SpinControl_Key( menulist_s *s, int key )
  772. {
  773.     sfxHandle_t    sound;
  774.  
  775.     sound = 0;
  776.     switch (key)
  777.     {
  778.         case K_MOUSE1:
  779.             s->curvalue++;
  780.             if (s->curvalue >= s->numitems)
  781.                 s->curvalue = 0;
  782.             sound = menu_move_sound;
  783.             break;
  784.         
  785.         case K_KP_LEFTARROW:
  786.         case K_LEFTARROW:
  787.             if (s->curvalue > 0)
  788.             {
  789.                 s->curvalue--;
  790.                 sound = menu_move_sound;
  791.             }
  792.             else
  793.                 sound = menu_buzz_sound;
  794.             break;
  795.  
  796.         case K_KP_RIGHTARROW:
  797.         case K_RIGHTARROW:
  798.             if (s->curvalue < s->numitems-1)
  799.             {
  800.                 s->curvalue++;
  801.                 sound = menu_move_sound;
  802.             }
  803.             else
  804.                 sound = menu_buzz_sound;
  805.             break;
  806.     }
  807.  
  808.     if ( sound && s->generic.callback )
  809.         s->generic.callback( s, QM_ACTIVATED );
  810.  
  811.     return (sound);
  812. }
  813.  
  814. /*
  815. =================
  816. SpinControl_Draw
  817. =================
  818. */
  819. static void SpinControl_Draw( menulist_s *s )
  820. {
  821.     float *color;
  822.     int    x,y;
  823.     int    style;
  824.     qboolean focus;
  825.  
  826.     x = s->generic.x;
  827.     y =    s->generic.y;
  828.  
  829.     style = UI_SMALLFONT;
  830.     focus = (s->generic.parent->cursor == s->generic.menuPosition);
  831.  
  832.     if ( s->generic.flags & QMF_GRAYED )
  833.         color = text_color_disabled;
  834.     else if ( focus )
  835.     {
  836.         color = text_color_highlight;
  837.         style |= UI_PULSE;
  838.     }
  839.     else if ( s->generic.flags & QMF_BLINK )
  840.     {
  841.         color = text_color_highlight;
  842.         style |= UI_BLINK;
  843.     }
  844.     else
  845.         color = text_color_normal;
  846.  
  847.     if ( focus )
  848.     {
  849.         // draw cursor
  850.         UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); 
  851.         UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
  852.     }
  853.  
  854.     UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
  855.     UI_DrawString( x + SMALLCHAR_WIDTH, y, s->itemnames[s->curvalue], style|UI_LEFT, color );
  856. }
  857.  
  858. /*
  859. =================
  860. ScrollList_Init
  861. =================
  862. */
  863. static void ScrollList_Init( menulist_s *l )
  864. {
  865.     l->oldvalue = 0;
  866.     l->curvalue = 0;
  867.     l->top      = 0;
  868.  
  869.     if( !l->columns ) {
  870.         l->columns = 1;
  871.         l->seperation = 0;
  872.     }
  873.     else if( !l->seperation ) {
  874.         l->seperation = 3;
  875.     }
  876.  
  877.     l->generic.left   =    l->generic.x;
  878.     l->generic.top    = l->generic.y;    
  879.     l->generic.right  =    l->generic.x + ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH;
  880.     l->generic.bottom =    l->generic.y + l->height * SMALLCHAR_HEIGHT;
  881. }
  882.  
  883. /*
  884. =================
  885. ScrollList_Key
  886. =================
  887. */
  888. sfxHandle_t ScrollList_Key( menulist_s *l, int key )
  889. {
  890.     int    x;
  891.     int    y;
  892.     int    i;
  893.     int    j;    
  894.     int    c;
  895.     int    cursorx;
  896.     int    cursory;
  897.     int    column;
  898.     int    index;
  899.  
  900.     switch (key)
  901.     {
  902.         case K_MOUSE1:
  903.             if (l->generic.flags & QMF_HASMOUSEFOCUS)
  904.             {
  905.                 // check scroll region
  906.                 x = l->generic.x;
  907.                 y = l->generic.y;
  908.                 if (UI_CursorInRect( x, y, ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH, l->height*SMALLCHAR_HEIGHT ))
  909.                 {
  910.                     cursorx = (uis.cursorx - x)/SMALLCHAR_WIDTH;
  911.                     column = cursorx / (l->width + l->seperation);
  912.                     cursory = (uis.cursory - y)/SMALLCHAR_HEIGHT;
  913.                     index = column * l->height + cursory;
  914.                     if (l->top + index < l->numitems)
  915.                     {
  916.                         l->oldvalue = l->curvalue;
  917.                         l->curvalue = l->top + index;
  918.  
  919.                         if (l->oldvalue != l->curvalue && l->generic.callback)
  920.                         {
  921.                             l->generic.callback( l, QM_GOTFOCUS );
  922.                             return (menu_move_sound);
  923.                         }
  924.                     }
  925.                 }
  926.             
  927.                 // absorbed, silent sound effect
  928.                 return (menu_null_sound);
  929.             }
  930.             break;
  931.  
  932.         case K_KP_HOME:
  933.         case K_HOME:
  934.             l->oldvalue = l->curvalue;
  935.             l->curvalue = 0;
  936.             l->top      = 0;
  937.  
  938.             if (l->oldvalue != l->curvalue && l->generic.callback)
  939.             {
  940.                 l->generic.callback( l, QM_GOTFOCUS );
  941.                 return (menu_move_sound);
  942.             }
  943.             return (menu_buzz_sound);
  944.  
  945.         case K_KP_END:
  946.         case K_END:
  947.             l->oldvalue = l->curvalue;
  948.             l->curvalue = l->numitems-1;
  949.             if( l->columns > 1 ) {
  950.                 c = (l->curvalue / l->height + 1) * l->height;
  951.                 l->top = c - (l->columns * l->height);
  952.             }
  953.             else {
  954.                 l->top = l->curvalue - (l->height - 1);
  955.             }
  956.             if (l->top < 0)
  957.                 l->top = 0;            
  958.  
  959.             if (l->oldvalue != l->curvalue && l->generic.callback)
  960.             {
  961.                 l->generic.callback( l, QM_GOTFOCUS );
  962.                 return (menu_move_sound);
  963.             }
  964.             return (menu_buzz_sound);
  965.  
  966.         case K_PGUP:
  967.         case K_KP_PGUP:
  968.             if( l->columns > 1 ) {
  969.                 return menu_null_sound;
  970.             }
  971.  
  972.             if (l->curvalue > 0)
  973.             {
  974.                 l->oldvalue = l->curvalue;
  975.                 l->curvalue -= l->height-1;
  976.                 if (l->curvalue < 0)
  977.                     l->curvalue = 0;
  978.                 l->top = l->curvalue;
  979.                 if (l->top < 0)
  980.                     l->top = 0;
  981.  
  982.                 if (l->generic.callback)
  983.                     l->generic.callback( l, QM_GOTFOCUS );
  984.  
  985.                 return (menu_move_sound);
  986.             }
  987.             return (menu_buzz_sound);
  988.  
  989.         case K_PGDN:
  990.         case K_KP_PGDN:
  991.             if( l->columns > 1 ) {
  992.                 return menu_null_sound;
  993.             }
  994.  
  995.             if (l->curvalue < l->numitems-1)
  996.             {
  997.                 l->oldvalue = l->curvalue;
  998.                 l->curvalue += l->height-1;
  999.                 if (l->curvalue > l->numitems-1)
  1000.                     l->curvalue = l->numitems-1;
  1001.                 l->top = l->curvalue - (l->height-1);
  1002.                 if (l->top < 0)
  1003.                     l->top = 0;
  1004.  
  1005.                 if (l->generic.callback)
  1006.                     l->generic.callback( l, QM_GOTFOCUS );
  1007.  
  1008.                 return (menu_move_sound);
  1009.             }
  1010.             return (menu_buzz_sound);
  1011.  
  1012.         case K_KP_UPARROW:
  1013.         case K_UPARROW:
  1014.             if( l->curvalue == 0 ) {
  1015.                 return menu_buzz_sound;
  1016.             }
  1017.  
  1018.             l->oldvalue = l->curvalue;
  1019.             l->curvalue--;
  1020.  
  1021.             if( l->curvalue < l->top ) {
  1022.                 if( l->columns == 1 ) {
  1023.                     l->top--;
  1024.                 }
  1025.                 else {
  1026.                     l->top -= l->height;
  1027.                 }
  1028.             }
  1029.  
  1030.             if( l->generic.callback ) {
  1031.                 l->generic.callback( l, QM_GOTFOCUS );
  1032.             }
  1033.  
  1034.             return (menu_move_sound);
  1035.  
  1036.         case K_KP_DOWNARROW:
  1037.         case K_DOWNARROW:
  1038.             if( l->curvalue == l->numitems - 1 ) {
  1039.                 return menu_buzz_sound;
  1040.             }
  1041.  
  1042.             l->oldvalue = l->curvalue;
  1043.             l->curvalue++;
  1044.  
  1045.             if( l->curvalue >= l->top + l->columns * l->height ) {
  1046.                 if( l->columns == 1 ) {
  1047.                     l->top++;
  1048.                 }
  1049.                 else {
  1050.                     l->top += l->height;
  1051.                 }
  1052.             }
  1053.  
  1054.             if( l->generic.callback ) {
  1055.                 l->generic.callback( l, QM_GOTFOCUS );
  1056.             }
  1057.  
  1058.             return menu_move_sound;
  1059.  
  1060.         case K_KP_LEFTARROW:
  1061.         case K_LEFTARROW:
  1062.             if( l->columns == 1 ) {
  1063.                 return menu_null_sound;
  1064.             }
  1065.  
  1066.             if( l->curvalue < l->height ) {
  1067.                 return menu_buzz_sound;
  1068.             }
  1069.  
  1070.             l->oldvalue = l->curvalue;
  1071.             l->curvalue -= l->height;
  1072.  
  1073.             if( l->curvalue < l->top ) {
  1074.                 l->top -= l->height;
  1075.             }
  1076.  
  1077.             if( l->generic.callback ) {
  1078.                 l->generic.callback( l, QM_GOTFOCUS );
  1079.             }
  1080.  
  1081.             return menu_move_sound;
  1082.  
  1083.         case K_KP_RIGHTARROW:
  1084.         case K_RIGHTARROW:
  1085.             if( l->columns == 1 ) {
  1086.                 return menu_null_sound;
  1087.             }
  1088.  
  1089.             c = l->curvalue + l->height;
  1090.  
  1091.             if( c >= l->numitems ) {
  1092.                 return menu_buzz_sound;
  1093.             }
  1094.  
  1095.             l->oldvalue = l->curvalue;
  1096.             l->curvalue = c;
  1097.  
  1098.             if( l->curvalue > l->top + l->columns * l->height - 1 ) {
  1099.                 l->top += l->height;
  1100.             }
  1101.  
  1102.             if( l->generic.callback ) {
  1103.                 l->generic.callback( l, QM_GOTFOCUS );
  1104.             }
  1105.  
  1106.             return menu_move_sound;
  1107.     }
  1108.  
  1109.     // cycle look for ascii key inside list items
  1110.     if ( !Q_isprint( key ) )
  1111.         return (0);
  1112.  
  1113.     // force to lower for case insensitive compare
  1114.     if ( Q_isupper( key ) )
  1115.     {
  1116.         key -= 'A' - 'a';
  1117.     }
  1118.  
  1119.     // iterate list items
  1120.     for (i=1; i<=l->numitems; i++)
  1121.     {
  1122.         j = (l->curvalue + i) % l->numitems;
  1123.         c = l->itemnames[j][0];
  1124.         if ( Q_isupper( c ) )
  1125.         {
  1126.             c -= 'A' - 'a';
  1127.         }
  1128.  
  1129.         if (c == key)
  1130.         {
  1131.             // set current item, mimic windows listbox scroll behavior
  1132.             if (j < l->top)
  1133.             {
  1134.                 // behind top most item, set this as new top
  1135.                 l->top = j;
  1136.             }
  1137.             else if (j > l->top+l->height-1)
  1138.             {
  1139.                 // past end of list box, do page down
  1140.                 l->top = (j+1) - l->height;
  1141.             }
  1142.             
  1143.             if (l->curvalue != j)
  1144.             {
  1145.                 l->oldvalue = l->curvalue;
  1146.                 l->curvalue = j;
  1147.                 if (l->generic.callback)
  1148.                     l->generic.callback( l, QM_GOTFOCUS );
  1149.                 return ( menu_move_sound );            
  1150.             }
  1151.  
  1152.             return (menu_buzz_sound);
  1153.         }
  1154.     }
  1155.  
  1156.     return (menu_buzz_sound);
  1157. }
  1158.  
  1159. /*
  1160. =================
  1161. ScrollList_Draw
  1162. =================
  1163. */
  1164. void ScrollList_Draw( menulist_s *l )
  1165. {
  1166.     int            x;
  1167.     int            y;
  1168.     int            i;
  1169.     int            base;
  1170.     int            column;
  1171.     float*        color;
  1172.     qboolean    hasfocus;
  1173.     int            style;
  1174.  
  1175.     hasfocus = (l->generic.parent->cursor == l->generic.menuPosition);
  1176.  
  1177.     x =    l->generic.x;
  1178.     for( column = 0; column < l->columns; column++ ) {
  1179.         y =    l->generic.y;
  1180.         base = l->top + column * l->height;
  1181.         for( i = base; i < base + l->height; i++) {
  1182.             if (i >= l->numitems)
  1183.                 break;
  1184.  
  1185.             if (i == l->curvalue)
  1186.             {
  1187.                 UI_FillRect(x-2,y,l->width*SMALLCHAR_WIDTH,SMALLCHAR_HEIGHT+2,listbar_color);
  1188.                 color = text_color_highlight;
  1189.  
  1190.                 if (hasfocus)
  1191.                     style = UI_PULSE|UI_LEFT|UI_SMALLFONT;
  1192.                 else
  1193.                     style = UI_LEFT|UI_SMALLFONT;
  1194.             }
  1195.             else
  1196.             {
  1197.                 color = text_color_normal;
  1198.                 style = UI_LEFT|UI_SMALLFONT;
  1199.             }
  1200.  
  1201.             UI_DrawString(
  1202.                 x,
  1203.                 y,
  1204.                 l->itemnames[i],
  1205.                 style,
  1206.                 color);
  1207.  
  1208.             y += SMALLCHAR_HEIGHT;
  1209.         }
  1210.         x += (l->width + l->seperation) * SMALLCHAR_WIDTH;
  1211.     }
  1212. }
  1213.  
  1214. /*
  1215. =================
  1216. Menu_AddItem
  1217. =================
  1218. */
  1219. void Menu_AddItem( menuframework_s *menu, void *item )
  1220. {
  1221.     menucommon_s    *itemptr;
  1222.  
  1223.     if (menu->nitems >= MAX_MENUITEMS)
  1224.         trap_Error ("Menu_AddItem: excessive items");
  1225.  
  1226.     menu->items[menu->nitems] = item;
  1227.     ((menucommon_s*)menu->items[menu->nitems])->parent        = menu;
  1228.     ((menucommon_s*)menu->items[menu->nitems])->menuPosition  = menu->nitems;
  1229.     ((menucommon_s*)menu->items[menu->nitems])->flags        &= ~QMF_HASMOUSEFOCUS;
  1230.  
  1231.     // perform any item specific initializations
  1232.     itemptr = (menucommon_s*)item;
  1233.     if (!(itemptr->flags & QMF_NODEFAULTINIT))
  1234.     {
  1235.         switch (itemptr->type)
  1236.         {
  1237.             case MTYPE_ACTION:
  1238.                 Action_Init((menuaction_s*)item);
  1239.                 break;
  1240.  
  1241.             case MTYPE_FIELD:
  1242.                 MenuField_Init((menufield_s*)item);
  1243.                 break;
  1244.  
  1245.             case MTYPE_SPINCONTROL:
  1246.                 SpinControl_Init((menulist_s*)item);
  1247.                 break;
  1248.  
  1249.             case MTYPE_RADIOBUTTON:
  1250.                 RadioButton_Init((menuradiobutton_s*)item);
  1251.                 break;
  1252.  
  1253.             case MTYPE_SLIDER:
  1254.                 Slider_Init((menuslider_s*)item);
  1255.                 break;
  1256.  
  1257.             case MTYPE_BITMAP:
  1258.                 Bitmap_Init((menubitmap_s*)item);
  1259.                 break;
  1260.  
  1261.             case MTYPE_TEXT:
  1262.                 Text_Init((menutext_s*)item);
  1263.                 break;
  1264.  
  1265.             case MTYPE_SCROLLLIST:
  1266.                 ScrollList_Init((menulist_s*)item);
  1267.                 break;
  1268.  
  1269.             case MTYPE_PTEXT:
  1270.                 PText_Init((menutext_s*)item);
  1271.                 break;
  1272.  
  1273.             case MTYPE_BTEXT:
  1274.                 BText_Init((menutext_s*)item);
  1275.                 break;
  1276.  
  1277.             default:
  1278.                 trap_Error( va("Menu_Init: unknown type %d", itemptr->type) );
  1279.         }
  1280.     }
  1281.  
  1282.     menu->nitems++;
  1283. }
  1284.  
  1285. /*
  1286. =================
  1287. Menu_CursorMoved
  1288. =================
  1289. */
  1290. void Menu_CursorMoved( menuframework_s *m )
  1291. {
  1292.     void (*callback)( void *self, int notification );
  1293.     
  1294.     if (m->cursor_prev == m->cursor)
  1295.         return;
  1296.  
  1297.     if (m->cursor_prev >= 0 && m->cursor_prev < m->nitems)
  1298.     {
  1299.         callback = ((menucommon_s*)(m->items[m->cursor_prev]))->callback;
  1300.         if (callback)
  1301.             callback(m->items[m->cursor_prev],QM_LOSTFOCUS);
  1302.     }
  1303.     
  1304.     if (m->cursor >= 0 && m->cursor < m->nitems)
  1305.     {
  1306.         callback = ((menucommon_s*)(m->items[m->cursor]))->callback;
  1307.         if (callback)
  1308.             callback(m->items[m->cursor],QM_GOTFOCUS);
  1309.     }
  1310. }
  1311.  
  1312. /*
  1313. =================
  1314. Menu_SetCursor
  1315. =================
  1316. */
  1317. void Menu_SetCursor( menuframework_s *m, int cursor )
  1318. {
  1319.     if (((menucommon_s*)(m->items[cursor]))->flags & (QMF_GRAYED|QMF_INACTIVE))
  1320.     {
  1321.         // cursor can't go there
  1322.         return;
  1323.     }
  1324.  
  1325.     m->cursor_prev = m->cursor;
  1326.     m->cursor      = cursor;
  1327.  
  1328.     Menu_CursorMoved( m );
  1329. }
  1330.  
  1331. /*
  1332. =================
  1333. Menu_SetCursorToItem
  1334. =================
  1335. */
  1336. void Menu_SetCursorToItem( menuframework_s *m, void* ptr )
  1337. {
  1338.     int    i;
  1339.  
  1340.     for (i=0; i<m->nitems; i++)
  1341.     {
  1342.         if (m->items[i] == ptr)
  1343.         {
  1344.             Menu_SetCursor( m, i );
  1345.             return;
  1346.         }
  1347.     }
  1348. }
  1349.  
  1350. /*
  1351. ** Menu_AdjustCursor
  1352. **
  1353. ** This function takes the given menu, the direction, and attempts
  1354. ** to adjust the menu's cursor so that it's at the next available
  1355. ** slot.
  1356. */
  1357. void Menu_AdjustCursor( menuframework_s *m, int dir ) {
  1358.     menucommon_s    *item = NULL;
  1359.     qboolean        wrapped = qfalse;
  1360.  
  1361. wrap:
  1362.     while ( m->cursor >= 0 && m->cursor < m->nitems ) {
  1363.         item = ( menucommon_s * ) m->items[m->cursor];
  1364.         if (( item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE) ) ) {
  1365.             m->cursor += dir;
  1366.         }
  1367.         else {
  1368.             break;
  1369.         }
  1370.     }
  1371.  
  1372.     if ( dir == 1 ) {
  1373.         if ( m->cursor >= m->nitems ) {
  1374.             if ( m->wrapAround ) {
  1375.                 if ( wrapped ) {
  1376.                     m->cursor = m->cursor_prev;
  1377.                     return;
  1378.                 }
  1379.                 m->cursor = 0;
  1380.                 wrapped = qtrue;
  1381.                 goto wrap;
  1382.             }
  1383.             m->cursor = m->cursor_prev;
  1384.         }
  1385.     }
  1386.     else {
  1387.         if ( m->cursor < 0 ) {
  1388.             if ( m->wrapAround ) {
  1389.                 if ( wrapped ) {
  1390.                     m->cursor = m->cursor_prev;
  1391.                     return;
  1392.                 }
  1393.                 m->cursor = m->nitems - 1;
  1394.                 wrapped = qtrue;
  1395.                 goto wrap;
  1396.             }
  1397.             m->cursor = m->cursor_prev;
  1398.         }
  1399.     }
  1400. }
  1401.  
  1402. /*
  1403. =================
  1404. Menu_Draw
  1405. =================
  1406. */
  1407. void Menu_Draw( menuframework_s *menu )
  1408. {
  1409.     int                i;
  1410.     menucommon_s    *itemptr;
  1411.  
  1412.     // draw menu
  1413.     for (i=0; i<menu->nitems; i++)
  1414.     {
  1415.         itemptr = (menucommon_s*)menu->items[i];
  1416.  
  1417.         if (itemptr->flags & QMF_HIDDEN)
  1418.             continue;
  1419.  
  1420.         if (itemptr->ownerdraw)
  1421.         {
  1422.             // total subclassing, owner draws everything
  1423.             itemptr->ownerdraw( itemptr );
  1424.         }    
  1425.         else 
  1426.         {
  1427.             switch (itemptr->type)
  1428.             {    
  1429.                 case MTYPE_RADIOBUTTON:
  1430.                     RadioButton_Draw( (menuradiobutton_s*)itemptr );
  1431.                     break;
  1432.  
  1433.                 case MTYPE_FIELD:
  1434.                     MenuField_Draw( (menufield_s*)itemptr );
  1435.                     break;
  1436.         
  1437.                 case MTYPE_SLIDER:
  1438.                     Slider_Draw( (menuslider_s*)itemptr );
  1439.                     break;
  1440.  
  1441.                 case MTYPE_SPINCONTROL:
  1442.                     SpinControl_Draw( (menulist_s*)itemptr );
  1443.                     break;
  1444.         
  1445.                 case MTYPE_ACTION:
  1446.                     Action_Draw( (menuaction_s*)itemptr );
  1447.                     break;
  1448.         
  1449.                 case MTYPE_BITMAP:
  1450.                     Bitmap_Draw( (menubitmap_s*)itemptr );
  1451.                     break;
  1452.  
  1453.                 case MTYPE_TEXT:
  1454.                     Text_Draw( (menutext_s*)itemptr );
  1455.                     break;
  1456.  
  1457.                 case MTYPE_SCROLLLIST:
  1458.                     ScrollList_Draw( (menulist_s*)itemptr );
  1459.                     break;
  1460.                 
  1461.                 case MTYPE_PTEXT:
  1462.                     PText_Draw( (menutext_s*)itemptr );
  1463.                     break;
  1464.  
  1465.                 case MTYPE_BTEXT:
  1466.                     BText_Draw( (menutext_s*)itemptr );
  1467.                     break;
  1468.  
  1469.                 default:
  1470.                     trap_Error( va("Menu_Draw: unknown type %d", itemptr->type) );
  1471.             }
  1472.         }
  1473. #ifndef NDEBUG
  1474.         if( uis.debug ) {
  1475.             int    x;
  1476.             int    y;
  1477.             int    w;
  1478.             int    h;
  1479.  
  1480.             if( !( itemptr->flags & QMF_INACTIVE ) ) {
  1481.                 x = itemptr->left;
  1482.                 y = itemptr->top;
  1483.                 w = itemptr->right - itemptr->left + 1;
  1484.                 h =    itemptr->bottom - itemptr->top + 1;
  1485.  
  1486.                 if (itemptr->flags & QMF_HASMOUSEFOCUS) {
  1487.                     UI_DrawRect(x, y, w, h, colorYellow );
  1488.                 }
  1489.                 else {
  1490.                     UI_DrawRect(x, y, w, h, colorWhite );
  1491.                 }
  1492.             }
  1493.         }
  1494. #endif
  1495.     }
  1496.  
  1497.     itemptr = Menu_ItemAtCursor( menu );
  1498.     if ( itemptr && itemptr->statusbar)
  1499.         itemptr->statusbar( ( void * ) itemptr );
  1500. }
  1501.  
  1502. /*
  1503. =================
  1504. Menu_ItemAtCursor
  1505. =================
  1506. */
  1507. void *Menu_ItemAtCursor( menuframework_s *m )
  1508. {
  1509.     if ( m->cursor < 0 || m->cursor >= m->nitems )
  1510.         return 0;
  1511.  
  1512.     return m->items[m->cursor];
  1513. }
  1514.  
  1515. /*
  1516. =================
  1517. Menu_ActivateItem
  1518. =================
  1519. */
  1520. sfxHandle_t Menu_ActivateItem( menuframework_s *s, menucommon_s* item ) {
  1521.     if ( item->callback ) {
  1522.         item->callback( item, QM_ACTIVATED );
  1523.         if( !( item->flags & QMF_SILENT ) ) {
  1524.             return menu_move_sound;
  1525.         }
  1526.     }
  1527.  
  1528.     return 0;
  1529. }
  1530.  
  1531. /*
  1532. =================
  1533. Menu_DefaultKey
  1534. =================
  1535. */
  1536. sfxHandle_t Menu_DefaultKey( menuframework_s *m, int key )
  1537. {
  1538.     sfxHandle_t        sound = 0;
  1539.     menucommon_s    *item;
  1540.     int                cursor_prev;
  1541.  
  1542.     // menu system keys
  1543.     switch ( key )
  1544.     {
  1545.         case K_MOUSE2:
  1546.         case K_ESCAPE:
  1547.             UI_PopMenu();
  1548.             return menu_out_sound;
  1549.     }
  1550.  
  1551.     if (!m || !m->nitems)
  1552.         return 0;
  1553.  
  1554.     // route key stimulus to widget
  1555.     item = Menu_ItemAtCursor( m );
  1556.     if (item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE)))
  1557.     {
  1558.         switch (item->type)
  1559.         {
  1560.             case MTYPE_SPINCONTROL:
  1561.                 sound = SpinControl_Key( (menulist_s*)item, key );
  1562.                 break;
  1563.  
  1564.             case MTYPE_RADIOBUTTON:
  1565.                 sound = RadioButton_Key( (menuradiobutton_s*)item, key );
  1566.                 break;
  1567.  
  1568.             case MTYPE_SLIDER:
  1569.                 sound = Slider_Key( (menuslider_s*)item, key );
  1570.                 break;
  1571.  
  1572.             case MTYPE_SCROLLLIST:
  1573.                 sound = ScrollList_Key( (menulist_s*)item, key );
  1574.                 break;
  1575.  
  1576.             case MTYPE_FIELD:
  1577.                 sound = MenuField_Key( (menufield_s*)item, &key );
  1578.                 break;
  1579.         }
  1580.  
  1581.         if (sound) {
  1582.             // key was handled
  1583.             return sound;        
  1584.         }
  1585.     }
  1586.  
  1587.     // default handling
  1588.     switch ( key )
  1589.     {
  1590. #ifndef NDEBUG
  1591.         case K_F11:
  1592.             uis.debug ^= 1;
  1593.             break;
  1594.  
  1595.         case K_F12:
  1596.             trap_Cmd_ExecuteText(EXEC_APPEND, "screenshot\n");
  1597.             break;
  1598. #endif
  1599.         case K_KP_UPARROW:
  1600.         case K_UPARROW:
  1601.             cursor_prev    = m->cursor;
  1602.             m->cursor_prev = m->cursor;
  1603.             m->cursor--;
  1604.             Menu_AdjustCursor( m, -1 );
  1605.             if ( cursor_prev != m->cursor ) {
  1606.                 Menu_CursorMoved( m );
  1607.                 sound = menu_move_sound;
  1608.             }
  1609.             break;
  1610.  
  1611.         case K_TAB:
  1612.         case K_KP_DOWNARROW:
  1613.         case K_DOWNARROW:
  1614.             cursor_prev    = m->cursor;
  1615.             m->cursor_prev = m->cursor;
  1616.             m->cursor++;
  1617.             Menu_AdjustCursor( m, 1 );
  1618.             if ( cursor_prev != m->cursor ) {
  1619.                 Menu_CursorMoved( m );
  1620.                 sound = menu_move_sound;
  1621.             }
  1622.             break;
  1623.  
  1624.         case K_MOUSE1:
  1625.         case K_MOUSE3:
  1626.             if (item)
  1627.                 if ((item->flags & QMF_HASMOUSEFOCUS) && !(item->flags & (QMF_GRAYED|QMF_INACTIVE)))
  1628.                     return (Menu_ActivateItem( m, item ));
  1629.             break;
  1630.  
  1631.         case K_JOY1:
  1632.         case K_JOY2:
  1633.         case K_JOY3:
  1634.         case K_JOY4:
  1635.         case K_AUX1:
  1636.         case K_AUX2:
  1637.         case K_AUX3:
  1638.         case K_AUX4:
  1639.         case K_AUX5:
  1640.         case K_AUX6:
  1641.         case K_AUX7:
  1642.         case K_AUX8:
  1643.         case K_AUX9:
  1644.         case K_AUX10:
  1645.         case K_AUX11:
  1646.         case K_AUX12:
  1647.         case K_AUX13:
  1648.         case K_AUX14:
  1649.         case K_AUX15:
  1650.         case K_AUX16:
  1651.         case K_KP_ENTER:
  1652.         case K_ENTER:
  1653.             if (item)
  1654.                 if (!(item->flags & (QMF_MOUSEONLY|QMF_GRAYED|QMF_INACTIVE)))
  1655.                     return (Menu_ActivateItem( m, item ));
  1656.             break;
  1657.     }
  1658.  
  1659.     return sound;
  1660. }
  1661.  
  1662. /*
  1663. =================
  1664. Menu_Cache
  1665. =================
  1666. */
  1667. void Menu_Cache( void )
  1668. {
  1669.     uis.charset            = trap_R_RegisterShaderNoMip( "gfx/2d/bigchars" );
  1670.     uis.charsetProp        = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" );
  1671.     uis.charsetPropGlow    = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" );
  1672.     uis.charsetPropB    = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" );
  1673.     uis.cursor          = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" );
  1674.     uis.rb_on           = trap_R_RegisterShaderNoMip( "menu/art/switch_on" );
  1675.     uis.rb_off          = trap_R_RegisterShaderNoMip( "menu/art/switch_off" );
  1676.  
  1677.     uis.whiteShader = trap_R_RegisterShaderNoMip( "white" );
  1678.     if ( uis.glconfig.hardwareType == GLHW_RAGEPRO ) {
  1679.         // the blend effect turns to shit with the normal 
  1680.         uis.menuBackShader    = trap_R_RegisterShaderNoMip( "menubackRagePro" );
  1681.     } else {
  1682.         uis.menuBackShader    = trap_R_RegisterShaderNoMip( "menuback" );
  1683.     }
  1684.     uis.menuBackNoLogoShader = trap_R_RegisterShaderNoMip( "menubacknologo" );
  1685.  
  1686.     menu_in_sound    = trap_S_RegisterSound( "sound/misc/menu1.wav" );
  1687.     menu_move_sound    = trap_S_RegisterSound( "sound/misc/menu2.wav" );
  1688.     menu_out_sound    = trap_S_RegisterSound( "sound/misc/menu3.wav" );
  1689.     menu_buzz_sound    = trap_S_RegisterSound( "sound/misc/menu4.wav" );
  1690.  
  1691.     // need a nonzero sound, make an empty sound for this
  1692.     menu_null_sound = -1;
  1693.  
  1694.     sliderBar = trap_R_RegisterShaderNoMip( "menu/art/slider2" );
  1695.     sliderButton_0 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_0" );
  1696.     sliderButton_1 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_1" );
  1697. }
  1698.     
  1699.